package view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;

import model.AbstractPlayer;
import model.Connected4Game;
import model.GameConfiguration;

public class Board extends JPanel implements Observer {
	private static final long serialVersionUID = 1L;

	private ObservableBoard obsBoard;
	private int hoveredColumn = -1;
	private Connected4Game game;
	private GameConfiguration config;

	/**
	 * Stato della board
	 */
	private BoardState state;

	public int getHoveredColumn() {
		return hoveredColumn;
	}

	public ObservableBoard getObservableBoard() {
		return obsBoard;

	}

	/**
	 * Consente di costruire una nuova area di gioco di dimensioni prestabilite
	 * 
	 * @param width
	 *            Larghezza dell'area di gioco
	 * @param height
	 *            Altezza dell'area di gioco
	 */
	public Board(GameConfiguration config) {
		this.config = config;
		this.setSize(GraphicsUtils.getBoardWidth(config.getCols()),
				GraphicsUtils.getBoardHeight(config.getRows()));
		this.hoveredColumn = -1;

		this.obsBoard = new ObservableBoard();
		// inizializzo il JPanel
		init();

		// Inizializzo il gioco.
		// Inizializzo i giocatori
		AbstractPlayer players[] = config.createAbstractPlayers(obsBoard);

		game = new Connected4Game(config.getRows(), config.getCols(),
				players[0], players[1], false);
		game.addObserver(Board.this);
		

		// Inizializzo lo stato
		this.state = new PlayerTurnState(config, this, 1, game.getBoard()
				.getBoard(), game.getP1().getDescription(), game.getP2()
				.getDescription(), game.getLastPlayedRow(),
				game.getLastPlayedCol());
		
		//Avvio il gioco
		game.start();

	}

	/**
	 * Esegue tutte le operazioni di inizializzazione sul JPanel
	 */
	private void init() {
		this.setFocusable(true);
		this.setBackground(Color.BLACK);
		this.setDoubleBuffered(true);
		MouseAdapter ma = new MAdapter();
		this.addMouseListener(ma);
		this.addMouseMotionListener(ma);
	}

	// Il metodo consente di disegnare al di sopra dell'area di gioco usando
	// l'oggetto Graphics passato come parametro. Il metodo e' richiamato
	// automaticmente dal componente JPanel alla sua inizializzazione e poi ogni
	// volta che viene chaimato il metodo repaint.
	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);

		Graphics2D g2d = (Graphics2D) g;
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		// Disegno la gradica in base allo stato
		state.paintGame(g2d);

		Toolkit.getDefaultToolkit().sync();

		g.dispose();

	}

	private class MAdapter extends MouseAdapter {
		@Override
		public void mousePressed(MouseEvent e) {
			state.mousePressed(e);
		}

		@Override
		public void mouseMoved(MouseEvent e) {
			state.mouseMoved(e);
		}

	}
	


	public void columnHovered(int col) {
		hoveredColumn = col;
		repaint();
	}

	class ObservableBoard extends Observable {
		public void columnSelected(int col) {
			setChanged();
			notifyObservers(col);
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		switch (game.getGameState()) {

		case PLAYING:
			if (game.getNextTurnPlayer().getColor() == Color.RED)
				state = new PlayerTurnState(config, this, 1, game.getBoard()
						.getBoard(), game.getP1().getDescription(), game
						.getP2().getDescription(), game.getLastPlayedRow(),
						game.getLastPlayedCol());
			else
				state = new PlayerTurnState(config, this, 2, game.getBoard()
						.getBoard(), game.getP1().getDescription(), game
						.getP2().getDescription(), game.getLastPlayedRow(),
						game.getLastPlayedCol());
			break;
		case VICTORY_1:
		case VICTORY_2:
		case DRAW:
			state = new EndGameState(config, this, game.getBoard().getBoard(),
					game.getP1().getDescription(), game.getP2()
							.getDescription(), game.getGameState(),
					game.getLastPlayedRow(), game.getLastPlayedCol());
			break;

		}

		repaint();
	}

	public void stop() {
		game.stop();
		
	}
}
